#pragma once
#include<iostream>
#include<vector>
#include<mutex>
#include<unordered_map>
#include<fstream>
#include"util.hpp"
#include"log.hpp"

namespace Index_ns
{

    struct DocInfo{
        std::string title;
        std::string content;
        std::string url;
        uint64_t id;
    };


    struct InvertInfo{
        uint64_t id;
        std::string word;
        int weight;
        
        InvertInfo():weight(0){}
    };



    typedef std::vector<InvertInfo> InvertList;
    static const std::string seq  = "\3";
    class Index
    {
        private:
          static std::mutex _mtx;
          static Index* _instance;
          

          std::vector<DocInfo> _Forward_Index;
          std::unordered_map<std::string,InvertList> _InvertListMap;//word与InvertList的映射
        private:
            Index(){};
            Index(const Index&) = delete;
            Index operator=(const Index&)=delete;
        public:
            static Index* GetInstance()
            {
                if(nullptr == _instance)
                {
                    _mtx.lock();
                    if(nullptr == _instance)
                    {
                        _instance = new Index;
                    }
                    _mtx.unlock();
                }

                return _instance;
            }

            DocInfo* ForwardIndex(uint64_t id)//提供正排索引方法
            {
                if(id > _Forward_Index.size())
                {
                    LOG(ERROR,"正排搜索失败,不存在id = %d 的文档",id); 
                    return nullptr;
                }
                return &_Forward_Index[id];
            }

            InvertList* InvertIndex(const std::string& word)//倒排索引方法
            {
                auto iter = _InvertListMap.find(word);
                if(iter == _InvertListMap.end())
                {
                    LOG(ERROR,"倒排搜索失败,不存在word =%s的关键词",word.c_str());
                    return nullptr;
                }

                return &_InvertListMap[word];
            }

            bool BulidIndex(const std::string contentpath)//构建正排索引与倒排索引
            {
                //1.首先读取内容将内容按照行分割出来存入DocInfo中,并构建正排与倒排索引
                std::ifstream ifs(contentpath,std::ios::in | std::ios::binary);
                std::string line;
                int cnt = 0;
                while(std::getline(ifs,line))
                {
                    DocInfo* doc = BulidForwardIndex(line);
                    if(doc == nullptr)
                        continue;
                    if(!BuildInvertIndex(doc))
                        continue;
                    cnt++;
                    
                    if(cnt % 50 == 0)
                    {
                        LOG(INFO,"构建索引的个数为：%d",cnt); 
                    }
                }
            }
            ~Index();

            private:
                DocInfo* BulidForwardIndex(const std::string& line)
                {
                    //1.分割line
                    std::vector<std::string> res;
                    Util_ns::String_Util::Spilt(line,&res,seq);

                    if(3 != res.size())
                        return nullptr;
                    
                    //2.填充Docinfo
                    DocInfo doc;
                    doc.title = res[0];
                    doc.content = res[1];
                    doc.url = res[2];
                    doc.id = _Forward_Index.size();

                    //3.将doc插入到正排索引数组中
                    _Forward_Index.emplace_back(std::move(doc));

                    //返回当前构建的Doc
                    return &_Forward_Index.back();
                }

                bool BuildInvertIndex(DocInfo* doc)
                {
                    //1.提取doc title和content的关键词
                    std::vector<std::string> title_key;
                    Util_ns::Jieba_Util::CutString(doc->title,&title_key);


                    struct word_cnt{
                        int cnt_title;
                        int cnt_content;
                        word_cnt():cnt_content(0),cnt_title(0){}
                    };
                    //1.1统计每个词的词频
                    std::unordered_map<std::string,word_cnt> word_map_cnt;
                    for(auto s:title_key)
                    {
                        boost::to_lower(s);
                        word_map_cnt[s].cnt_title++;
                    }

                    //2.提取出doc content的关键词
                    std::vector<std::string> content_key;
                    Util_ns::Jieba_Util::CutString(doc->content,&content_key);

                    //2.1统计每个词的词频
                    for(auto s:content_key)
                    {
                        boost::to_lower(s);
                        word_map_cnt[s].cnt_content++;
                    }
#define TW 10
#define CW 1
                    //3.遍历哈希表建立索引
                    for(auto& word_pair:word_map_cnt)
                    {
                         InvertInfo _invertelem;
                         _invertelem.id = doc->id;
                         _invertelem.weight = word_pair.second.cnt_title * TW + word_pair.second.cnt_content * CW;
                         _invertelem.word = word_pair.first;

                         InvertList& _InvertList = _InvertListMap[word_pair.first];
                         _InvertList.emplace_back(std::move(_invertelem));
                    }

                    return true;
                }
    };

    std::mutex Index::_mtx;   
    Index* Index::_instance = nullptr;
}
